package kernel;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A set of fibers
 *
 */
public class FiberSet {

	public static final String LINES = "LINES";
	public static final String POINTS = "POINTS";
	public static final String START_POINTS = "START_POINTS";
	public static final String END_POINTS = "END_POINTS";
	
	public static final int PRINT_2D_MATRIX = 0;
	public static final int PRINT_CONNECTIVITY_FILE = 1;

	public static final int LINE = 0;
	public static final int POINT = 1;
	public static final int START_POINT = 2;
	public static final int END_POINT = 3;

	public static final int FILEPATH = 4;
	public static final int COLOR = 5;
	public static final int RENDER_COUNT = 6;
	public static final int RENDER_TYPE = 7;

	private ArrayList<Fiber> fibers;
	private ArrayList<Fiber> newFibers;
	private String name;
	private String filePath;
	private double[] boundingMin;
	private double[] boundingMax;
	private double fibersetBoundngBoxWidth;
	private double fibersetBoundingBoxHeight;
	private double fibersetBoundingBoxDepth;
	private double[] center;
	private ROI[][][] roiMatrix;
	private ROI2D[][] connectivityMatrix;
	private double roiResolution;
	private int roiCounterMax;
	private int roiConnectivityCounterMax;
	private int connectivityMatrixMax;

	private boolean drawLines = true;
	private boolean drawPoints = false;
	private boolean drawStartPoints = false;
	private boolean drawEndPoints = false;
	private int fiberRenderCount = 1;
	
	private boolean withLog;

	/**
	 * Basic constructor
	 */
	public FiberSet()
	{
		withLog = false;
		fibers = new ArrayList<Fiber>();
		boundingMin = new double[]{Double.MAX_VALUE, Double.MAX_VALUE,Double.MAX_VALUE};
		boundingMax = new double[]{Double.MIN_VALUE, Double.MIN_VALUE,Double.MIN_VALUE};
		center = new double[3];
	}

	/**
	 * Initialize a ROI matrix with a given resolution
	 * @param nRoiResolution roi resolution in mm
	 */
	public void createRoiMatrix(double nRoiResolution)
	{
		roiResolution = nRoiResolution;
		int roiWidth = (int)(fibersetBoundngBoxWidth/roiResolution);
		int roiHeight = (int)(fibersetBoundingBoxHeight/roiResolution);
		int roiDepth = (int)(fibersetBoundingBoxDepth/roiResolution);

		roiMatrix = new ROI[roiWidth + 1][roiHeight + 1][roiDepth + 1];

	}

	/**
	 * Returns the ROI matrix
	 * @return ROI matrix
	 */
	public ROI[][][] getRoiMatrix() {
		return roiMatrix;
	}
	/**
	 * Calculates the ROI matrix. It gives each ROI the fiber id that passes by.
	 */
	public void calculateRoiMatrix()
	{
		for(int fiberId = 0; fiberId < fibers.size(); fiberId++)
		{
			Fiber f = fibers.get(fiberId);
			ArrayList<double[]> fiberPoints = f.getPoints();
			for(double[] point : fiberPoints)
			{
				int posX = (int)((point[0] - boundingMin[0]) / roiResolution);
				int posY = (int)((point[1] - boundingMin[1]) / roiResolution);
				int posZ = (int)((point[2] - boundingMin[2]) / roiResolution);

				if(roiMatrix[posX][posY][posZ] == null)
				{
					roiMatrix[posX][posY][posZ] = new ROI(posX, posY, posZ);
				}
				roiMatrix[posX][posY][posZ].addFiberId(fiberId);
				roiMatrix[posX][posY][posZ].addFiber(f);
			}
		}
	}

	/**
	 * Sets the fiber render type
	 * @param renderType is a boolean[]{LINES, POINTS, START_POINTS, END_POINTS}
	 */
	public void setFiberRenderType(boolean[] renderType)
	{
		drawLines = renderType[LINE];
		drawPoints = renderType[POINT];
		drawStartPoints = renderType[START_POINT];
		drawEndPoints = renderType[END_POINT];
	}
	/**
	 * Sets the fiber render type in a String
	 * @param renderType a String with the rendering type. It must contain at least LINE, POINT, START_POINTS, or END_POINTS
	 */
	public void setFiberRenderType(String renderType)
	{
		String[] renderTypes = renderType.split(" ");
		for(String rType : renderTypes)
		{
			rType = getRendererType(rType);
			if(rType.equals(LINES))
			{
				drawLines = true;
			}
			else if(rType.equals(POINTS))
			{
				drawPoints = true;
			}
			else if(rType.equals(START_POINTS))
			{
				drawStartPoints = true;
			}
			else if(rType.equals(END_POINTS))
			{
				drawEndPoints = true;
			}
		}

	}

	/**
	 * Sets the number of fibers to render
	 * @param fiberRenderCount number of fibers to render
	 */
	public void setFiberRenderCount(int fiberRenderCount)
	{
		this.fiberRenderCount = fiberRenderCount;
	}

	/**
	 * Creates a fiber set with the given fibers
	 * @param fibers fibers that belong to this set
	 */
	public FiberSet(ArrayList<Fiber> fibers)
	{
		this();
		this.fibers = fibers;
	}

	/**
	 * Set the option to draw the fibers as lines
	 * @param option 
	 */
	public void drawLines(boolean option)
	{
		drawLines = option;
	}

	/**
	 * Set the option to draw the fibers as points
	 * @param option
	 */
	public void drawPoints(boolean option)
	{
		drawPoints = option;
	}

	/**
	 * Sets the option to draw the fibers start points
	 * @param option
	 */
	public void drawStartPoints(boolean option)
	{
		drawStartPoints = option;
	}

	/**
	 * Set the option to draw the fibers end points
	 * @param option
	 */
	public void drawEndPoints(boolean option)
	{
		drawEndPoints = option;
	}

	/**
	 * Sets the name of the fiber set. By default is the short name of the file path for the fiber set
	 * @param filepath 
	 */
	public void setName(String filepath)
	{
		File file = new File(filepath);
		this.filePath = file.getAbsolutePath();
		this.name = file.getName();
	}

	/**
	 * Set the fibers set
	 * @param fibers
	 */
	public void setFibers(ArrayList<Fiber> fibers)
	{
		this.fibers = fibers;
	}

	/**
	 * Returns the filepath
	 * @return the filePath
	 */
	public String getFilePath() {
		return filePath;
	}

	/**
	 * Returns the name of the set
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Get the rendering setup for the fiber set. Which kind of visualization will use
	 * @return lines, points, start points, end points
	 */
	public boolean[] getRenderSetup()
	{
		return new boolean[]{drawLines, drawPoints, drawStartPoints, drawEndPoints};
	}

	/**
	 * Return the fiber set
	 * @return the fiber set
	 */
	public ArrayList<Fiber> getFibers() {
		return fibers;
	}

	/**
	 * Returns the set of fibers generated after loading the new points
	 * @return set of fibers with the new points
	 */
	public ArrayList<Fiber> getNewFibers()
	{
		return newFibers;
	}

	/**
	 * Size of the fiber set
	 * @return
	 */
	public int size() {
		return fibers.size();
	}

	/**
	 * Returns the number of fibers to render
	 * @return
	 */
	public int getFiberRenderCount() {
		return fiberRenderCount;
	}

	/**
	 * Evaluates the raw type and returns a valid rendering type
	 * @param rawType rendering type
	 * @return valid rendering type
	 */
	public static String getRendererType(String rawType)
	{
		if(!rawType.equalsIgnoreCase(LINES) && 
				!rawType.equalsIgnoreCase(POINTS) &&
				!rawType.equalsIgnoreCase(START_POINTS) &&
				!rawType.equalsIgnoreCase(END_POINTS))
		{
			return LINES;
		}
		return rawType.toUpperCase();
	}

	/**
	 * Reads a file that contains the fiber set in vtk format
	 * @param file vtk file that contains the fiber set
	 * @param color rendering color for the fiber set
	 * @throws Exception if the file could not be found
	 */
	public void readFile(File file, Color color) throws Exception
	{
		setName(file.getAbsolutePath());
		System.out.println("Start Loading: " + file.getAbsolutePath());
		BufferedReader buffReader = new BufferedReader(new FileReader(file));
		String line = "";

		boolean readingpoints = false;
		boolean readinglines = false;
		ArrayList<Double> vertices = new ArrayList<Double>();
		ArrayList<double[]> points = new ArrayList<double[]>();
		int lineNumber = 1;
		while ((line =buffReader.readLine())!= null)
		{
			if(line.contains("POINTS"))
			{
//				System.out.println("File info -> " + line);
				readingpoints = true;
				continue;
			}
			if(line.contains("LINES"))
			{
//				System.out.println("File info -> " + line);
				readingpoints = false;
				readinglines = true;

				if(withLog)
					System.out.println("Vertices: " + vertices.size());
				for(int i = 0; i < vertices.size(); i+=3)
				{
					try {
						double x = vertices.get(i);
						double y = vertices.get(i + 1);
						double z = vertices.get(i + 2);

						if(x < boundingMin[0])
						{
							boundingMin[0] = x;
						}
						if(x > boundingMax[0])
						{
							boundingMax[0] = x;
						}
						if(y < boundingMin[1])
						{
							boundingMin[1] = y;
						}
						if(y > boundingMax[1])
						{
							boundingMax[1] = y;
						}
						if(z < boundingMin[2])
						{
							boundingMin[2] = z;
						}
						if(z > boundingMax[2])
						{
							boundingMax[2] = z;
						}

						points.add(new double[]{
								x,
								y,
								z
						});



					} catch (Exception e) {
						System.err.println("Vertices " + i + " could not be read");
					}
				}
				continue;
			}
			if(line.contains("POINT_DATA"))
			{

				break;
			}

			if(readingpoints)
			{
				String[] parser = line.split("\\s");
				if(parser.length % 3 != 0)
				{
					System.err.println("Error in parsing: " + lineNumber + " --- " + parser.length);
				}
				for(int i = 0; i < parser.length; i++)
				{
					try {
						vertices.add(Double.parseDouble(parser[i]));
					} catch (Exception e) {
						System.err.println("Error line " + lineNumber + " parser size: " + parser.length);
					}
				}
			}

			if(readinglines)
			{ 
				String[] parser = line.split(" ");
				ArrayList<double[]> line_i = new ArrayList<double[]>();
				for(int i = 1; i < parser.length && parser.length != 1; i++)
				{
					int index = Integer.parseInt(parser[i]);
					try {
						line_i.add(points.get(index));
					} catch (Exception e) {
						continue;
					}
				}
				if(line_i.size() != 0)
				{
					Fiber newFiber = new Fiber(line_i, color);
					newFiber.setFiberName("Fiber_"+fibers.size());
					newFiber.setFiberId(fibers.size());
					fibers.add(newFiber);
				}
			}
			lineNumber++;
		}

		buffReader.close();
		if(withLog)
		{
			System.out.println("Points: " + points.size());
			System.out.println("Lines:  " + size());
			System.out.println("Bounds:\n\tXmin,Xmax: (" +boundingMin[0] + ", " + boundingMax[0] + ")");
			System.out.println("\tYmin,Ymax: (" + boundingMin[1] + ", " + boundingMax[1] + ")");
			System.out.println("\tZmin,Zmax: (" + boundingMin[2] + ", " + boundingMax[2] + ")");
		}


		fibersetBoundngBoxWidth = (boundingMax[0] - boundingMin[0]);
		fibersetBoundingBoxHeight = (boundingMax[1] - boundingMin[1]);
		fibersetBoundingBoxDepth = (boundingMax[2] - boundingMin[2]);

		center[0] =  boundingMin[0] + fibersetBoundngBoxWidth/2;
		center[1] =  boundingMin[1] + fibersetBoundingBoxHeight/2;
		center[2] =  boundingMin[2] + fibersetBoundingBoxDepth/2;

		if(withLog)
			System.out.println("Width = " + fibersetBoundngBoxWidth + " Height: " + fibersetBoundingBoxHeight + " Depth = " + fibersetBoundingBoxDepth + " Center: " + center[0] + ", " + center[1] + ", " + center[2]);

		System.out.println("End Loading  : " + file.getAbsolutePath());
	}

	/**
	 * Set the new bounding box for the fibers and calculation of grid
	 * @param minX minimum value in X
	 * @param minY minimum value in Y
	 * @param minZ minimum value in Z
	 * @param maxX maximum value in X
	 * @param maxY maximum value in Y
	 * @param maxZ maximum value in Z
	 */
	public void setBounds(double minX, double minY, double minZ, double maxX, double maxY, double maxZ)
	{
		if(minX <= boundingMin[0] && minY <= boundingMin[1] && minZ <= boundingMin[2] &&
				maxX >= boundingMax[0] && maxY >= boundingMax[1] && maxZ >= boundingMax[2])
		{
			boundingMin[0] = minX;
			boundingMin[1] = minY;
			boundingMin[2] = minZ;
			boundingMax[0] = maxX;
			boundingMax[1] = maxY;
			boundingMax[2] = maxZ;
			System.out.println("Bounds updated");
		}
		else
		{
			System.out.println("New bounds does not contain old ones");
		}
		System.out.println("Bounds:\n\tXmin,Xmax: (" +boundingMin[0] + ", " + boundingMax[0] + ")");
		System.out.println("\tYmin,Ymax: (" + boundingMin[1] + ", " + boundingMax[1] + ")");
		System.out.println("\tZmin,Zmax: (" + boundingMin[2] + ", " + boundingMax[2] + ")");
	}
	/**
	 * Writes a file with only the points of the fiber set
	 * @param file file to be written
	 * @throws Exception if the file does not exist
	 */
	public void writeFile(File file) throws Exception
	{
		System.out.println("Writing file " + file.getAbsolutePath());
		FileWriter out = new FileWriter(file);
		for(Fiber f : fibers)
		{
			for(double[] p : f.getPoints())
			{
				out.write(p[0] + " " + p[1] + " " + p[2] + "\n");
			}
		}
		out.close();
		System.out.println("End writing file " + file.getAbsolutePath());
	}


	/**
	 * Returns the minimum point of the bounding box for the fiber set
	 * @return minimum point
	 */
	public double[] getBoundingMin() {
		return boundingMin;
	}

	/**
	 * Returns the maximum point of the bounding box for the fiber set
	 * @return maximum point
	 */
	public double[] getBoundingMax() {
		return boundingMax;
	}

	/**
	 * Returns the width of the bounding box of the fiber set
	 * @return width
	 */
	public double getWidth() {
		return fibersetBoundngBoxWidth;
	}

	/**
	 * Returns the height of the bounding box of the fiber set
	 * @return height
	 */
	public double getHeight() {
		return fibersetBoundingBoxHeight;
	}

	/**
	 * Returns the depth of the bounding box of the fiber set
	 * @return depth
	 */
	public double getDepth() {
		return fibersetBoundingBoxDepth;
	}

	/**
	 * Returns the center of the bounding box of the fiber set
	 * @return center
	 */
	public double[] getCenter()
	{
		return center;
	}

	/**
	 * Creates a new set of fibers with the given points. the number of fibers is the same
	 * @param newPoints new fiber points
	 */
	public void createNewSet(ArrayList<double[]> newPoints)
	{
		newFibers = new ArrayList<Fiber>();
		int index = 0;
		for(Fiber f : fibers)
		{
			int fiberSize = f.getPoints().size();
			Fiber newFiber = new Fiber(subList(newPoints, index, index + fiberSize), Color.YELLOW);
			newFibers.add(newFiber);
			index += fiberSize;
		}
	}

	/**
	 * Returns a sublist of points
	 * @param points original list
	 * @param from initial index
	 * @param to end index
	 * @return
	 */
	private ArrayList<double[]> subList(ArrayList<double[]> points, int from, int to)
	{
		ArrayList<double[]> subList = new ArrayList<double[]>();
		for(int i = from; i < to; i++)
		{
			subList.add(points.get(i));
		}

		return subList;
	}

	/**
	 * Returns the maximum number of fibers that pass by a ROI
	 * @return maximum
	 */
	public int getRoiCounterMax() {
		return roiCounterMax;
	}

	/**
	 * Set the maximum number of fibers that pass by a ROI
	 * @param roiCounterMax counter
	 */
	public void setRoiCounterMax(int roiCounterMax) {
		this.roiCounterMax = roiCounterMax;
	}

	/**
	 * Returns the maximum number of ROIs that has connection with another ROI
	 * @return maximum max
	 */
	public int getRoiConnectivityCounterMax() {
		return roiConnectivityCounterMax;
	}

	/**
	 * Sets the maximum number of ROIs that has connection with another ROI
	 * @param roiConnectivityCounterMax counter
	 */
	public void setRoiConnectivityCounterMax(int roiConnectivityCounterMax) {
		this.roiConnectivityCounterMax = roiConnectivityCounterMax;
	}

	/**
	 * Calculates the connectivity matrix.<br>
	 * Is the matrix where each ROI has a set of ROIs that has at least 1 fiber in common
	 */
	public void calculateConectivity3DMatrix()
	{
		System.out.println("Calculating ROI connectivity matrix");

		boolean printTest = false;
		BufferedWriter out = null;
		if(printTest)
		{
			File file = new File("File/"+name+"_fibersConnectivity_test.csv");

			try {
				out = new BufferedWriter(new FileWriter(file));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		int percentage = 0;
		for(int fiberIndex = 0; fiberIndex < fibers.size(); fiberIndex++)
		{
			if(((fiberIndex + 1) % (fibers.size() / 10)) == 0)
			{
				System.out.println(percentage + "% of fibers calculated");
				percentage += 10;
			}
			Set<ROI> connectivityROI_i = new HashSet<ROI>();
			for(int i = 0; i < roiMatrix.length; i ++)
			{
				for(int j = 0; j < roiMatrix[0].length; j ++)
				{
					for(int k = 0; k < roiMatrix[0][0].length; k ++)
					{

						ROI roi_ijk = roiMatrix[i][j][k]; 
						if( roi_ijk != null && roi_ijk.containFiber(fiberIndex))
						{
							connectivityROI_i.add(roi_ijk);

							Iterator<ROI> it_ROI = connectivityROI_i.iterator();
							while(it_ROI.hasNext())
							{
								ROI roi_i = it_ROI.next(); 
								if(!roi_i.equals(roi_ijk))
								{
									roi_i.addROI(roi_ijk);
									roi_ijk.addROI(roi_i);
								}
							}
						}
					}
				}
			}
			if(printTest && fiberIndex < 5)
			{
				try {
					out.write(String.valueOf(fiberIndex) + "," + connectivityROI_i.size() + "\n");
					Iterator<ROI> it_ROI = connectivityROI_i.iterator();
					while(it_ROI.hasNext())
					{
						ROI roi_i = it_ROI.next();
						int[] roiPos = roi_i.getRoiPosition();
						out.write("##,##,x:"+roiPos[0]+"y:"+roiPos[1]+"z:"+roiPos[2] + "\n");
						Iterator<ROI> it_ROI_2 = roi_i.getConnectivitySet().iterator();
						out.write("##,##," + roi_i.getConnectivitySet().size());
						while(it_ROI_2.hasNext())
						{
							int[] roiPos_2 = it_ROI_2.next().getRoiPosition();
							out.write(",x:"+roiPos_2[0]+"y:"+roiPos_2[1]+"z:"+roiPos_2[2]);
						}
						out.write("\n");
					}
					out.write("\n");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		if(printTest)
		{
			try {
				out.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("End Calculating ROI connectivity matrix");
	}

	/**
	 * Returns the width of the ROI matrix
	 * @return
	 */
	public int getROIWidth()
	{
		return roiMatrix.length;
	}
	/**
	 * Returns the height of the ROI matrix
	 * @return
	 */
	public int getROIHeight()
	{
		return roiMatrix[0].length;
	}
	/**
	 * Returns the depth of the ROI matrix
	 * @return
	 */
	public int getROIDepth()
	{
		return roiMatrix[0][0].length;
	}

	/**
	 * @return the connectivityMatrixMax
	 */
	public int getConnectivityMatrixMax() {
		return connectivityMatrixMax;
	}

	public void calculate2DConnectivityMatrix(int matrixWidth, int matrixHeight, int matrixDepth, File file, int printType)
	{
		System.out.println("Calculating 2D Connectivity matrix");
		connectivityMatrixMax = 0;
		if(matrixWidth == 0 && matrixHeight == 0 && matrixDepth == 0)
		{
			matrixWidth = roiMatrix.length;
			matrixHeight = roiMatrix[0].length;
			matrixDepth = roiMatrix[0][0].length;
		}

		int size = (matrixWidth * matrixHeight * matrixDepth);
		System.out.println("Matrix Size: " + size);
		int percentage = 0;
		int percentageStep = 5;
		connectivityMatrix = new ROI2D[size][size];
		
		boolean printConnectivityFile = false;
		boolean printMatrix2D = false;
		boolean withHeaders = true;
		switch (printType) {
		case PRINT_2D_MATRIX:
			printMatrix2D = true;
			break;
		case PRINT_CONNECTIVITY_FILE:
			printConnectivityFile = true;
			break;
		}
		
		BufferedWriter out = null;
		try {
			out =new BufferedWriter(new FileWriter(file));
		} catch (IOException e) {
			e.printStackTrace();
		}

		int index = 0;
		
		if(printMatrix2D && withHeaders)
		{
			try {
				out.write("ROIS");
				for(int i = 0; i < matrixWidth; i ++)
				{
					for(int j = 0; j < matrixHeight; j ++)
					{
						for(int k = 0; k < matrixDepth; k ++)
						{
							out.write(",<"+i + "|"+ j + "|" + k +">");
						}
					}
				}
				out.write("\n");
			} catch (IOException e) {
				e.printStackTrace();
			}
					
		}
		
		for(int i = 0; i < matrixWidth; i ++)
		{
			for(int j = 0; j < matrixHeight; j ++)
			{
				for(int k = 0; k < matrixDepth; k ++)
				{
					ROI roi_i = roiMatrix[i][j][k];
					int[] matrix2DRow = new int[size];
					int matrixRow = (matrixHeight * matrixDepth * i) + (matrixDepth * j) + k; 
					if(roi_i != null)
					{
						Set<ROI> roiConSet = roi_i.getConnectivitySet();
//						if(testPrint)
//						{
//							int[] roi_i_Pos= roi_i.getRoiPosition();
//							try {
//								out.write("[" + roi_i_Pos[0] + "|" + roi_i_Pos[1] + "|" + roi_i_Pos[2] + "],[" + i + "|" + j + "|" + k + "],------>," + (roiConSet == null) + ",");
//							} catch (IOException e) {
//								e.printStackTrace();
//							}
//						}
						if(roiConSet != null)
						{
							Iterator<ROI> it_ROI = roiConSet.iterator();
//							if(testPrint)
//							{
//								try {
//									out.write("," + roiConSet.size() + ",");
//								} catch (IOException e) {
//									e.printStackTrace();
//								}
//							}
							while(it_ROI.hasNext())
							{
								ROI roi_j = it_ROI.next();
								int[] roi_j_Pos = roi_j.getRoiPosition();
								int matrixColumn = (matrixHeight * matrixDepth * roi_j_Pos[0]) + (matrixDepth * roi_j_Pos[1]) + roi_j_Pos[2];  
								connectivityMatrix[matrixRow][matrixColumn] = new ROI2D(roi_i, roi_j);
								connectivityMatrix[matrixRow][matrixColumn].calculateFibers();
								matrix2DRow[matrixColumn] += connectivityMatrix[matrixRow][matrixColumn].getFibers().size();
								
								
								if(printConnectivityFile)
								{
									Set<Fiber> sharedFibers = connectivityMatrix[matrixRow][matrixColumn].getFibers();
									Map<Fiber, Integer> map_roi_i = roi_i.getFibers();
									Map<Fiber, Integer> map_roi_j = roi_j.getFibers();
									try {
										out.write("[" + i + "|" + j + "|" + k + "],[" + roi_j_Pos[0] + "|" + roi_j_Pos[1] + "|" + roi_j_Pos[2] + "]," + sharedFibers.size() + ",");
										for(Fiber f : sharedFibers)
										{
											if(map_roi_i.containsKey(f) && map_roi_j.containsKey(f))
											{
												out.write("," + String.valueOf(f.getFiberId()) + "|" + map_roi_i.get(f) + "|" + map_roi_j.get(f));
											}
											else if(map_roi_i.containsKey(f) && !map_roi_j.containsKey(f))
											{
												out.write("," + String.valueOf(f.getFiberId()) + "|" + map_roi_i.get(f) + "|" + 0);
											}
											else if(!map_roi_i.containsKey(f) && map_roi_j.containsKey(f))
											{
												out.write("," + String.valueOf(f.getFiberId()) + "|" + 0 + "|" + map_roi_j.get(f));
											}
											else
											{
												out.write("," + String.valueOf(f.getFiberId()) + "|" + 0 + "|" + 0);
											}
										}
										out.write("\n");
									} catch (IOException e) {
										e.printStackTrace();
									}
								}
							}	
						}
					}
					if(printMatrix2D)
					{
						try {
							if(withHeaders)
							{
								out.write("<"+i + "|"+ j + "|" + k +">,");
							}
							for(int indexRow = 0; indexRow < matrix2DRow.length; indexRow++)
							{
								int rowValue = matrix2DRow[indexRow];
								if(connectivityMatrixMax < rowValue)
								{
									connectivityMatrixMax = rowValue;
								}
								if(indexRow == 0)
								{
									out.write(String.valueOf(rowValue));
								}
								else
								{
									out.write(","+String.valueOf(rowValue));
								}
							}
							out.write("\n");
						} catch (IOException e) {
							e.printStackTrace();
						}	
					}
					if((index % (size * ((double)percentageStep/100.0))) == 0)
					{	
						System.out.println(percentage + "% of ROIS traversed. " + index + " ROI out of " + size);
						percentage += percentageStep;
					}
					
					index++;
				}	
			}
		}

		if(printConnectivityFile)
		{
			try {
				out.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		System.out.println("Matrix max: " + connectivityMatrixMax);
		System.out.println("End Calculating 2D Connectivity matrix");
	}

	/**
	 * @return the connectivityMatrix
	 */
	public int[][] getIntConnectivityMatrix() {
		connectivityMatrixMax = 0;
		int size = connectivityMatrix.length;
		int[][] conMatrix = new int[size][size];
		for(int i =0; i < size; i++)
		{
			for(int j = 0 ; j < size ; j++)
			{
				ROI2D roi2D = connectivityMatrix[i][j];
				if(roi2D != null)
				{
					roi2D.calculateFibers();
					conMatrix[i][j] = roi2D.getFibers().size();
					if(connectivityMatrixMax < conMatrix[i][j])
					{
						connectivityMatrixMax = conMatrix[i][j];
					}
				}
			}
		}
		return conMatrix;
	}
	
	public void writeVTKFile(File file, boolean newFiber) throws Exception
	{
		System.out.println("writing file: " + file.getAbsolutePath());
		BufferedWriter out = new BufferedWriter(new FileWriter(file));
		
		out.write("# vtk DataFile Version 3.0\n");
		out.write("vtk output\n");
		out.write("ASCII\n");
		out.write("DATASET POLYDATA\n");
		
		ArrayList<double[]> points = new ArrayList<double[]>();
		int fibersSize = 0;
		if(newFiber)
		{
			fibersSize = newFibers.size();
		}
		else
		{
			fibersSize = fibers.size();
		}
		int[] lines = new int[fibersSize];
		
		int i = 0;
		int size = 0;
		if(newFiber)
		{
			for(Fiber f : newFibers)
			{
				points.addAll(f.getPoints());
				lines[i] = f.getPoints().size();
				size += lines[i]; 
				i++;
			}
		}
		else
		{
			for(Fiber f : fibers)
			{
				points.addAll(f.getPoints());
				lines[i] = f.getPoints().size();
				size += lines[i]; 
				i++;
			}
		}
		
		out.write("POINTS " + points.size() + " float\n");
		
		int counter = 0;
		for(double[] point : points)
		{
			out.write(point[0] + " " + point[1] + " " + point[2] + " ");
			counter++;
			if(counter > 2)
			{
				out.write("\n");
				counter = 0;
			}
		}
		out.write("\nLINES " + fibersSize + " " + (fibersSize + size) + "\n");
		
		int lineCounter = 0;
		for(int j = 0; j < lines.length ; j++)
		{
			out.write(lines[j] + " ");
			for(int k = lineCounter; k < lineCounter + lines[j]; k++)
			{
				out.write(k + " ");
			}
			out.write("\n");
			lineCounter += lines[j];
		}
		
		out.write("\nPOINT_DATA " + points.size() + "\nCOLOR_SCALARS scalars 3\n");
		for(int k = 0; k < points.size() / 2; k++)
		{
			out.write("0 0 0 0 0 0\n");
		}
		
		out.close();
		System.out.println("end writing file: " + file.getAbsolutePath());
	}
	
	public void setLoggingOn()
	{
		withLog = true;
	}

	public static void main(String[] args)
	{
		int width = 3;
		int height = 4;
		int depth = 5;
		for(int i = 0; i < width; i++)
		{
			for(int j = 0; j < height; j++)
			{
				for(int k = 0; k < depth; k++)
				{
					int position = (height * depth * i) + (depth * j) + k;
					System.out.println(i + ", " + j + ", " + k + ": " + position);
				}	
			}	
		}
	}

	
}
